package com.mattgarner.jaddas.node.index;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;

public class BitmapIndexColumn {

	private byte[] bitmap;
	private int bitWidth;
	
	private final byte LZIP_FLAG = -99;
	
	public BitmapIndexColumn(int bitWidth) {
		this.bitWidth = bitWidth;
		bitmap = new byte[bitWidth/8+1];
	}
	
	public BitmapIndexColumn(byte[] bitmap) {
		this.bitmap = bitmap;
	}
	
	public final void setBit(int bit, boolean value) {
		int byteOffset = bit/8;
		int bitOffset = bit%8;

		// add an additional byte to our bitmap if necessary
		if(byteOffset >= bitmap.length) {
			byte[] largerBitmap = new byte[byteOffset+1];
			System.arraycopy(bitmap, 0, largerBitmap, 0, bitmap.length);
			bitmap = largerBitmap;
		}
		
		int setBit = 0;
		if(value) {
			setBit = 1;
		}		
		
		int bitMask = setBit<<bitOffset;
		
		int targetByte = bitmap[byteOffset];
		int newByte = targetByte | bitMask;
				
		bitmap[byteOffset] = (byte)newByte;
	}
	
	public final byte[] getBitmap() {
		return bitmap;
	}
	
	public final byte[] getCompressedBitmap() throws IndexException {
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			DataOutputStream buffer = new DataOutputStream(baos);
			
			// place magic compression flag
			buffer.writeByte(LZIP_FLAG);
			buffer.writeInt(bitmap.length);
			
			DeflaterOutputStream lzos = new DeflaterOutputStream(buffer);
			
			buffer.flush();
			
			// compress bitmap
			lzos.write(bitmap, 0, bitmap.length);
			
			return baos.toByteArray();
			
		} catch(IOException e) {
			throw new IndexException("getCompressedBitmap: " + e.getMessage());
		}
	}
	
	public final void setCompressedBitmap(byte[] compressedBitmap) throws IndexException {
		try {
			ByteArrayInputStream bais = new ByteArrayInputStream(compressedBitmap);
			DataInputStream buffer = new DataInputStream(bais);
			
			byte flag = buffer.readByte();
			
			// check for magic flag
			if(flag != LZIP_FLAG) {
				throw new IndexException("setCompressedBitmap: Not a compressed bitmap.");
			}
			
			int decompressedLength = buffer.readInt();
			InflaterInputStream lzis = new InflaterInputStream(buffer);
			
			// decompress bitmap
			lzis.read(bitmap, 0, decompressedLength);
			lzis.close();
			buffer.close();
			
			
		} catch(IOException e) {
			throw new IndexException("setCompressedBitmap: " + e.getMessage());
		}

	}
}
